home *** CD-ROM | disk | FTP | other *** search
/ FishMarket 1.0 / FishMarket v1.0.iso / fishies / 001-025 / disk_006 / microemacs / display.c < prev    next >
C/C++ Source or Header  |  1992-05-06  |  22KB  |  863 lines

  1. /*
  2.  * The functions in this file handle redisplay. There are two halves, the
  3.  * ones that update the virtual display screen, and the ones that make the
  4.  * physical display screen the same as the virtual display screen. These
  5.  * functions use hints that are left in the windows by the commands.
  6.  *
  7.  * REVISION HISTORY:
  8.  *
  9.  * ?    Steve Wilhite, 1-Dec-85
  10.  *      - massive cleanup on code.
  11.  */
  12.  
  13. #include        <stdio.h>
  14. #include        "ed.h"
  15.  
  16. #define WFDEBUG 0                       /* Window flag debug. */
  17.  
  18. typedef struct  VIDEO {
  19.         short   v_flag;                 /* Flags */
  20.         char    v_text[1];              /* Screen data. */
  21. }       VIDEO;
  22.  
  23. #define VFCHG   0x0001                  /* Changed. */
  24.  
  25. int     sgarbf  = TRUE;                 /* TRUE if screen is garbage */
  26. int     mpresf  = FALSE;                /* TRUE if message in last line */
  27. int     vtrow   = 0;                    /* Row location of SW cursor */
  28. int     vtcol   = 0;                    /* Column location of SW cursor */
  29. int     ttrow   = HUGE;                 /* Row location of HW cursor */
  30. int     ttcol   = HUGE;                 /* Column location of HW cursor */
  31.  
  32. VIDEO   **vscreen;                      /* Virtual screen. */
  33. VIDEO   **pscreen;                      /* Physical screen. */
  34.  
  35. /*
  36.  * Initialize the data structures used by the display code. The edge vectors
  37.  * used to access the screens are set up. The operating system's terminal I/O
  38.  * channel is set up. All the other things get initialized at compile time.
  39.  * The original window has "WFCHG" set, so that it will get completely
  40.  * redrawn on the first call to "update".
  41.  */
  42. vtinit()
  43. {
  44.     register int i;
  45.     register VIDEO *vp;
  46.  
  47.     (*term.t_open)();
  48.     vscreen = (VIDEO **) malloc(term.t_nrow*sizeof(VIDEO *));
  49.  
  50.     if (vscreen == NULL)
  51.         exit(1);
  52.  
  53.     pscreen = (VIDEO **) malloc(term.t_nrow*sizeof(VIDEO *));
  54.  
  55.     if (pscreen == NULL)
  56.         exit(1);
  57.  
  58.     for (i = 0; i < term.t_nrow; ++i)
  59.         {
  60.         vp = (VIDEO *) malloc(sizeof(VIDEO)+term.t_ncol);
  61.  
  62.         if (vp == NULL)
  63.             exit(1);
  64.  
  65.         vscreen[i] = vp;
  66.         vp = (VIDEO *) malloc(sizeof(VIDEO)+term.t_ncol);
  67.  
  68.         if (vp == NULL)
  69.             exit(1);
  70.  
  71.         pscreen[i] = vp;
  72.         }
  73. }
  74.  
  75. /*
  76.  * Clean up the virtual terminal system, in anticipation for a return to the
  77.  * operating system. Move down to the last line and clear it out (the next
  78.  * system prompt will be written in the line). Shut down the channel to the
  79.  * terminal.
  80.  */
  81. vttidy()
  82. {
  83.     movecursor(term.t_nrow, 0);
  84.     (*term.t_eeol)();
  85.     (*term.t_close)();
  86. }
  87.  
  88. /*
  89.  * Set the virtual cursor to the specified row and column on the virtual
  90.  * screen. There is no checking for nonsense values; this might be a good
  91.  * idea during the early stages.
  92.  */
  93. vtmove(row, col)
  94. {
  95.     vtrow = row;
  96.     vtcol = col;
  97. }
  98.  
  99. /*
  100.  * Write a character to the virtual screen. The virtual row and column are
  101.  * updated. If the line is too long put a "$" in the last column. This routine
  102.  * only puts printing characters into the virtual terminal buffers. Only
  103.  * column overflow is checked.
  104.  */
  105. vtputc(c)
  106.     int c;
  107. {
  108.     register VIDEO      *vp;
  109.  
  110.     vp = vscreen[vtrow];
  111.  
  112.     if (vtcol >= term.t_ncol)
  113.     {
  114.         vp->v_text[term.t_ncol - 1] = '$';
  115.     ++vtcol;
  116.     }
  117.     else if (c == '\t')
  118.         {
  119.         do
  120.             {
  121.             vtputc(' ');
  122.             }
  123.         while ((vtcol&0x07) != 0);
  124.         }
  125.     else if (c < 0x20 || c == 0x7F)
  126.         {
  127.         vtputc('^');
  128.         vtputc(c ^ 0x40);
  129.         }
  130.     else
  131.         vp->v_text[vtcol++] = c;                
  132. }
  133.  
  134. /*
  135.  * Erase from the end of the software cursor to the end of the line on which
  136.  * the software cursor is located.
  137.  */
  138. vteeol()
  139. {
  140.     register VIDEO      *vp;
  141.  
  142.     vp = vscreen[vtrow];
  143.     while (vtcol < term.t_ncol)
  144.         vp->v_text[vtcol++] = ' ';
  145. }
  146.  
  147. /*
  148.  * Make sure that the display is right. This is a three part process. First,
  149.  * scan through all of the windows looking for dirty ones. Check the framing,
  150.  * and refresh the screen. Second, make sure that "currow" and "curcol" are
  151.  * correct for the current window. Third, make the virtual and physical
  152.  * screens the same.
  153.  */
  154. update()
  155. {
  156.     register LINE *lp;
  157.     register WINDOW *wp;
  158.     register VIDEO *vp1;
  159.     register VIDEO *vp2;
  160.     register int i;
  161.     register int j;
  162.     register int c;
  163.  
  164.     wp = wheadp;
  165.  
  166.     while (wp != NULL)
  167.         {
  168.         /* Look at any window with update flags set on. */
  169.  
  170.         if (wp->w_flag != 0)
  171.             {
  172.             /* If not force reframe, check the framing. */
  173.  
  174.             if ((wp->w_flag & WFFORCE) == 0)
  175.                 {
  176.                 lp = wp->w_linep;
  177.  
  178.                 for (i = 0; i < wp->w_ntrows; ++i)
  179.                     {
  180.                     if (lp == wp->w_dotp)
  181.                         goto out;
  182.  
  183.                     if (lp == wp->w_bufp->b_linep)
  184.                         break;
  185.  
  186.                     lp = lforw(lp);
  187.                     }
  188.                 }
  189.  
  190.             /* Not acceptable, better compute a new value for the line at the
  191.              * top of the window. Then set the "WFHARD" flag to force full
  192.              * redraw.
  193.              */
  194.             i = wp->w_force;
  195.  
  196.             if (i > 0)
  197.                 {
  198.                 --i;
  199.  
  200.                 if (i >= wp->w_ntrows)
  201.                   i = wp->w_ntrows-1;
  202.                 }
  203.             else if (i < 0)
  204.                 {
  205.                 i += wp->w_ntrows;
  206.  
  207.                 if (i < 0)
  208.                     i = 0;
  209.                 }
  210.             else
  211.                 i = wp->w_ntrows/2;
  212.  
  213.             lp = wp->w_dotp;
  214.  
  215.             while (i != 0 && lback(lp) != wp->w_bufp->b_linep)
  216.                 {
  217.                 --i;
  218.                 lp = lback(lp);
  219.                 }
  220.  
  221.             wp->w_linep = lp;
  222.             wp->w_flag |= WFHARD;       /* Force full. */
  223.  
  224. out:
  225.             /* Try to use reduced update. Mode line update has its own special
  226.              * flag. The fast update is used if the only thing to do is within
  227.              * the line editing.
  228.              */
  229.             lp = wp->w_linep;
  230.             i = wp->w_toprow;
  231.  
  232.             if ((wp->w_flag & ~WFMODE) == WFEDIT)
  233.                 {
  234.                 while (lp != wp->w_dotp)
  235.                     {
  236.                     ++i;
  237.                     lp = lforw(lp);
  238.                     }
  239.  
  240.                 vscreen[i]->v_flag |= VFCHG;
  241.                 vtmove(i, 0);
  242.  
  243.                 for (j = 0; j < llength(lp); ++j)
  244.                     vtputc(lgetc(lp, j));
  245.  
  246.                 vteeol();
  247.                 }
  248.              else if ((wp->w_flag & (WFEDIT | WFHARD)) != 0)
  249.                 {
  250.                 while (i < wp->w_toprow+wp->w_ntrows)
  251.                     {
  252.                     vscreen[i]->v_flag |= VFCHG;
  253.                     vtmove(i, 0);
  254.  
  255.                     if (lp != wp->w_bufp->b_linep)
  256.                         {
  257.                         for (j = 0; j < llength(lp); ++j)
  258.                             vtputc(lgetc(lp, j));
  259.  
  260.                         lp = lforw(lp);
  261.                         }
  262.  
  263.                     vteeol();
  264.                     ++i;
  265.                     }
  266.                 }
  267. #if ~WFDEBUG
  268.             if ((wp->w_flag&WFMODE) != 0)
  269.                 modeline(wp);
  270.  
  271.             wp->w_flag  = 0;
  272.             wp->w_force = 0;
  273. #endif
  274.             }           
  275. #if WFDEBUG
  276.         modeline(wp);
  277.         wp->w_flag =  0;
  278.         wp->w_force = 0;
  279. #endif
  280.         wp = wp->w_wndp;
  281.         }
  282.  
  283.     /* Always recompute the row and column number of the hardware cursor. This
  284.      * is the only update for simple moves.
  285.      */
  286.     lp = curwp->w_linep;
  287.     currow = curwp->w_toprow;
  288.  
  289.     while (lp != curwp->w_dotp)
  290.         {
  291.         ++currow;
  292.         lp = lforw(lp);
  293.         }
  294.  
  295.     curcol = 0;
  296.     i = 0;
  297.  
  298.     while (i < curwp->w_doto)
  299.         {
  300.         c = lgetc(lp, i++);
  301.  
  302.         if (c == '\t')
  303.             curcol |= 0x07;
  304.         else if (c < 0x20 || c == 0x7F)
  305.             ++curcol;
  306.  
  307.         ++curcol;
  308.         }
  309.  
  310.     if (curcol >= term.t_ncol)          /* Long line. */
  311.         curcol = term.t_ncol-1;
  312.  
  313.     /* Special hacking if the screen is garbage. Clear the hardware screen,
  314.      * and update your copy to agree with it. Set all the virtual screen
  315.      * change bits, to force a full update.
  316.      */
  317.     if (sgarbf != FALSE)
  318.         {
  319.         for (i = 0; i < term.t_nrow; ++i)
  320.             {
  321.             vscreen[i]->v_flag |= VFCHG;
  322.             vp1 = pscreen[i];
  323.             for (j = 0; j < term.t_ncol; ++j)
  324.                 vp1->v_text[j] = ' ';
  325.             }
  326.  
  327.         movecursor(0, 0);               /* Erase the screen. */
  328.         (*term.t_eeop)();
  329.         sgarbf = FALSE;                 /* Erase-page clears */
  330.         mpresf = FALSE;                 /* the message area. */
  331.         }
  332.  
  333.     /* Make sure that the physical and virtual displays agree. Unlike before,
  334.      * the "updateline" code is only called with a line that has been updated
  335.      * for sure.
  336.      */
  337.     for (i = 0; i < term.t_nrow; ++i)
  338.         {
  339.         vp1 = vscreen[i];
  340.  
  341.         if ((vp1->v_flag&VFCHG) != 0)
  342.             {
  343.             vp1->v_flag &= ~VFCHG;
  344.             vp2 = pscreen[i];
  345.             updateline(i, &vp1->v_text[0], &vp2->v_text[0]);
  346.             }
  347.         }
  348.  
  349.     /* Finally, update the hardware cursor and flush out buffers. */
  350.  
  351.     movecursor(currow, curcol);
  352.     (*term.t_flush)();
  353. }
  354.  
  355. /*
  356.  * Update a single line. This does not know how to use insert or delete
  357.  * character sequences; we are using VT52 functionality. Update the physical
  358.  * row and column variables. It does try an exploit erase to end of line. The
  359.  * RAINBOW version of this routine uses fast video.
  360.  */
  361. updateline(row, vline, pline)
  362.     char vline[];
  363.     char pline[];
  364. {
  365. #if RAINBOW
  366.     register char *cp1;
  367.     register char *cp2;
  368.     register int nch;
  369.  
  370.     cp1 = &vline[0];                    /* Use fast video. */
  371.     cp2 = &pline[0];
  372.     putline(row+1, 1, cp1);
  373.     nch = term.t_ncol;
  374.  
  375.     do
  376.         {
  377.         *cp2 = *cp1;
  378.         ++cp2;
  379.         ++cp1;
  380.         }
  381.     while (--nch);
  382. #else
  383.     register char *cp1;
  384.     register char *cp2;
  385.     register char *cp3;
  386.     register char *cp4;
  387.     register char *cp5;
  388.     register int nbflag;
  389.  
  390.     cp1 = &vline[0];                    /* Compute left match.  */
  391.     cp2 = &pline[0];
  392.  
  393.     while (cp1!=&vline[term.t_ncol] && cp1[0]==cp2[0])
  394.         {
  395.         ++cp1;
  396.         ++cp2;
  397.         }
  398.  
  399.     /* This can still happen, even though we only call this routine on changed
  400.      * lines. A hard update is always done when a line splits, a massive
  401.      * change is done, or a buffer is displayed twice. This optimizes out most
  402.      * of the excess updating. A lot of computes are used, but these tend to
  403.      * be hard operations that do a lot of update, so I don't really care.
  404.      */
  405.     if (cp1 == &vline[term.t_ncol])             /* All equal. */
  406.         return;
  407.  
  408.     nbflag = FALSE;
  409.     cp3 = &vline[term.t_ncol];          /* Compute right match. */
  410.     cp4 = &pline[term.t_ncol];
  411.  
  412.     while (cp3[-1] == cp4[-1])
  413.         {
  414.         --cp3;
  415.         --cp4;
  416.         if (cp3[0] != ' ')              /* Note if any nonblank */
  417.             nbflag = TRUE;              /* in right match. */
  418.         }
  419.  
  420.     cp5 = cp3;
  421.  
  422.     if (nbflag == FALSE)                /* Erase to EOL ? */
  423.         {
  424.         while (cp5!=cp1 && cp5[-1]==' ')
  425.             --cp5;
  426.  
  427.         if (cp3-cp5 <= 3)               /* Use only if erase is */
  428.             cp5 = cp3;                  /* fewer characters. */
  429.         }
  430.  
  431.     movecursor(row, cp1-&vline[0]);     /* Go to start of line. */
  432.  
  433.     while (cp1 != cp5)                  /* Ordinary. */
  434.         {
  435.         (*term.t_putchar)(*cp1);
  436.         ++ttcol;
  437.         *cp2++ = *cp1++;
  438.         }
  439.  
  440.     if (cp5 != cp3)                     /* Erase. */
  441.         {
  442.         (*term.t_eeol)();
  443.         while (cp1 != cp3)
  444.             *cp2++ = *cp1++;
  445.         }
  446. #endif
  447. }
  448.  
  449. /*
  450.  * Redisplay the mode line for the window pointed to by the "wp". This is the
  451.  * only routine that has any idea of how the modeline is formatted. You can
  452.  * change the modeline format by hacking at this routine. Called by "update"
  453.  * any time there is a dirty window.
  454.  */
  455. modeline(wp)
  456.     WINDOW *wp;
  457. {
  458.     register char *cp;
  459.     register int c;
  460.     register int n;
  461.     register BUFFER *bp;
  462.  
  463.     n = wp->w_toprow+wp->w_ntrows;              /* Location. */
  464.     vscreen[n]->v_flag |= VFCHG;                /* Redraw next time. */
  465.     vtmove(n, 0);                               /* Seek to right line. */
  466.     vtputc('-');
  467.     bp = wp->w_bufp;
  468.  
  469.     if ((bp->b_flag&BFCHG) != 0)                /* "*" if changed. */
  470.         vtputc('*');
  471.     else
  472.         vtputc('-');
  473.  
  474.     n  = 2;
  475.     cp = " MicroEMACS -- ";                     /* Buffer name. */
  476.  
  477.     while ((c = *cp++) != 0)
  478.         {
  479.         vtputc(c);
  480.         ++n;
  481.         }
  482.  
  483.     cp = &bp->b_bname[0];
  484.  
  485.     while ((c = *cp++) != 0)
  486.         {
  487.         vtputc(c);
  488.         ++n;
  489.         }
  490.  
  491.     vtputc(' ');
  492.     ++n;
  493.  
  494.     if (bp->b_fname[0] != 0)            /* File name. */
  495.         {
  496.         cp = "-- File: ";
  497.  
  498.         while ((c = *cp++) != 0)
  499.             {
  500.             vtputc(c);
  501.             ++n;
  502.             }
  503.  
  504.         cp = &bp->b_fname[0];
  505.  
  506.         while ((c = *cp++) != 0)
  507.             {
  508.             vtputc(c);
  509.             ++n;
  510.             }
  511.  
  512.         vtputc(' ');
  513.         ++n;
  514.         }
  515.  
  516. #if WFDEBUG
  517.     vtputc('-');
  518.     vtputc((wp->w_flag&WFMODE)!=0  ? 'M' : '-');
  519.     vtputc((wp->w_flag&WFHARD)!=0  ? 'H' : '-');
  520.     vtputc((wp->w_flag&WFEDIT)!=0  ? 'E' : '-');
  521.     vtputc((wp->w_flag&WFMOVE)!=0  ? 'V' : '-');
  522.     vtputc((wp->w_flag&WFFORCE)!=0 ? 'F' : '-');
  523.     n += 6;
  524. #endif
  525.  
  526.     while (n < term.t_ncol)             /* Pad to full width. */
  527.         {
  528.         vtputc('-');
  529.         ++n;
  530.         }
  531. }
  532.  
  533. /*
  534.  * Send a command to the terminal to move the hardware cursor to row "row"
  535.  * and column "col". The row and column arguments are origin 0. Optimize out
  536.  * random calls. Update "ttrow" and "ttcol".
  537.  */
  538. movecursor(row, col)
  539.     {
  540.     if (row!=ttrow || col!=ttcol)
  541.         {
  542.         ttrow = row;
  543.         ttcol = col;
  544.         (*term.t_move)(row, col);
  545.         }
  546.     }
  547.  
  548. /*
  549.  * Erase the message line. This is a special routine because the message line
  550.  * is not considered to be part of the virtual screen. It always works
  551.  * immediately; the terminal buffer is flushed via a call to the flusher.
  552.  */
  553. mlerase()
  554.     {
  555.     movecursor(term.t_nrow, 0);
  556.     (*term.t_eeol)();
  557.     (*term.t_flush)();
  558.     mpresf = FALSE;
  559.     }
  560.  
  561. /*
  562.  * Ask a yes or no question in the message line. Return either TRUE, FALSE, or
  563.  * ABORT. The ABORT status is returned if the user bumps out of the question
  564.  * with a ^G. Used any time a confirmation is required.
  565.  */
  566. mlyesno(prompt)
  567.     char *prompt;
  568.     {
  569.     register int s;
  570.     char buf[64];
  571.  
  572.     for (;;)
  573.         {
  574.         strcpy(buf, prompt);
  575.         strcat(buf, " [y/n]? ");
  576.         s = mlreply(buf, buf, sizeof(buf));
  577.  
  578.         if (s == ABORT)
  579.             return (ABORT);
  580.  
  581.         if (s != FALSE)
  582.             {
  583.             if (buf[0]=='y' || buf[0]=='Y')
  584.                 return (TRUE);
  585.  
  586.             if (buf[0]=='n' || buf[0]=='N')
  587.                 return (FALSE);
  588.             }
  589.         }
  590.     }
  591.  
  592. /*
  593.  * Write a prompt into the message line, then read back a response. Keep
  594.  * track of the physical position of the cursor. If we are in a keyboard
  595.  * macro throw the prompt away, and return the remembered response. This
  596.  * lets macros run at full speed. The reply is always terminated by a carriage
  597.  * return. Handle erase, kill, and abort keys.
  598.  */
  599. mlreply(prompt, buf, nbuf)
  600.     char *prompt;
  601.     char *buf;
  602.     {
  603.     register int cpos;
  604.     register int i;
  605.     register int c;
  606.  
  607.     cpos = 0;
  608.  
  609.     if (kbdmop != NULL)
  610.         {
  611.         while ((c = *kbdmop++) != '\0')
  612.             buf[cpos++] = c;
  613.  
  614.         buf[cpos] = 0;
  615.  
  616.         if (buf[0] == 0)
  617.             return (FALSE);
  618.  
  619.         return (TRUE);
  620.         }
  621.  
  622.     mlwrite(prompt);
  623.  
  624.     for (;;)
  625.         {
  626.         c = (*term.t_getchar)();
  627.  
  628.         switch (c)
  629.             {
  630.             case 0x0D:                  /* Return, end of line */
  631.                 buf[cpos++] = 0;
  632.  
  633.                 if (kbdmip != NULL)
  634.                     {
  635.                     if (kbdmip+cpos > &kbdm[NKBDM-3])
  636.                         {
  637.                         ctrlg(FALSE, 0);
  638.                         (*term.t_flush)();
  639.                         return (ABORT);
  640.                         }
  641.  
  642.                     for (i=0; i<cpos; ++i)
  643.                         *kbdmip++ = buf[i];
  644.                     }
  645.  
  646.                 (*term.t_putchar)('\r');
  647.                 ttcol = 0;
  648.                 (*term.t_flush)();
  649.  
  650.                 if (buf[0] == 0)
  651.                     return (FALSE);
  652.  
  653.                 return (TRUE);
  654.  
  655.             case 0x07:                  /* Bell, abort */
  656.                 (*term.t_putchar)('^');
  657.                 (*term.t_putchar)('G');
  658.                 ttcol += 2;
  659.                 ctrlg(FALSE, 0);
  660.                 (*term.t_flush)();
  661.                 return (ABORT);
  662.  
  663.             case 0x7F:                  /* Rubout, erase */
  664.             case 0x08:                  /* Backspace, erase */
  665.                 if (cpos != 0)
  666.                     {
  667.                     (*term.t_putchar)('\b');
  668.                     (*term.t_putchar)(' ');
  669.                     (*term.t_putchar)('\b');
  670.                     --ttcol;
  671.  
  672.                     if (buf[--cpos] < 0x20)
  673.                         {
  674.                         (*term.t_putchar)('\b');
  675.                         (*term.t_putchar)(' ');
  676.                         (*term.t_putchar)('\b');
  677.                         --ttcol;
  678.                         }
  679.  
  680.                     (*term.t_flush)();
  681.                     }
  682.  
  683.                 break;
  684.  
  685.             case 0x15:                  /* C-U, kill */
  686.                 while (cpos != 0)
  687.                     {
  688.                     (*term.t_putchar)('\b');
  689.                     (*term.t_putchar)(' ');
  690.                     (*term.t_putchar)('\b');
  691.                     --ttcol;
  692.  
  693.                     if (buf[--cpos] < 0x20)
  694.                         {
  695.                         (*term.t_putchar)('\b');
  696.                         (*term.t_putchar)(' ');
  697.                         (*term.t_putchar)('\b');
  698.                         --ttcol;
  699.                         }
  700.                     }
  701.  
  702.                 (*term.t_flush)();
  703.                 break;
  704.  
  705.             default:
  706.                 if (cpos < nbuf-1)
  707.                     {
  708.                     buf[cpos++] = c;
  709.  
  710.                     if (c < ' ')
  711.                         {
  712.                         (*term.t_putchar)('^');
  713.                         ++ttcol;
  714.                         c ^= 0x40;
  715.                         }
  716.  
  717.                     (*term.t_putchar)(c);
  718.                     ++ttcol;
  719.                     (*term.t_flush)();
  720.                     }
  721.             }
  722.         }
  723.     }
  724.  
  725. /*
  726.  * Write a message into the message line. Keep track of the physical cursor
  727.  * position. A small class of printf like format items is handled. Assumes the
  728.  * stack grows down; this assumption is made by the "++" in the argument scan
  729.  * loop. Set the "message line" flag TRUE.
  730.  */
  731. mlwrite(fmt, arg)
  732.     char *fmt;
  733.     {
  734.     register int c;
  735.     register char *ap;
  736.  
  737.     movecursor(term.t_nrow, 0);
  738.     ap = (char *) &arg;
  739.     while ((c = *fmt++) != 0) {
  740.         if (c != '%') {
  741.             (*term.t_putchar)(c);
  742.             ++ttcol;
  743.             }
  744.         else
  745.             {
  746.             c = *fmt++;
  747.             switch (c) {
  748.                 case 'd':
  749.                     mlputi(*(int *)ap, 10);
  750.                     ap += sizeof(int);
  751.                     break;
  752.  
  753.                 case 'o':
  754.                     mlputi(*(int *)ap,  8);
  755.                     ap += sizeof(int);
  756.                     break;
  757.  
  758.                 case 'x':
  759.                     mlputi(*(int *)ap, 16);
  760.                     ap += sizeof(int);
  761.                     break;
  762.  
  763.                 case 'D':
  764.                     mlputli(*(long *)ap, 10);
  765.                     ap += sizeof(long);
  766.                     break;
  767.  
  768.                 case 's':
  769.                     mlputs(*(char **)ap);
  770.                     ap += sizeof(char *);
  771.                     break;
  772.  
  773.                 default:
  774.                     (*term.t_putchar)(c);
  775.                     ++ttcol;
  776.                 }
  777.             }
  778.         }
  779.     (*term.t_eeol)();
  780.     (*term.t_flush)();
  781.     mpresf = TRUE;
  782.     }
  783.  
  784. /*
  785.  * Write out a string. Update the physical cursor position. This assumes that
  786.  * the characters in the string all have width "1"; if this is not the case
  787.  * things will get screwed up a little.
  788.  */
  789. mlputs(s)
  790.     char *s;
  791.     {
  792.     register int c;
  793.  
  794.     while ((c = *s++) != 0)
  795.         {
  796.         (*term.t_putchar)(c);
  797.         ++ttcol;
  798.         }
  799.     }
  800.  
  801. /*
  802.  * Write out an integer, in the specified radix. Update the physical cursor
  803.  * position. This will not handle any negative numbers; maybe it should.
  804.  */
  805. mlputi(i, r)
  806.     {
  807.     register int q;
  808.     static char hexdigits[] = "0123456789ABCDEF";
  809.  
  810.     if (i < 0)
  811.         {
  812.         i = -i;
  813.         (*term.t_putchar)('-');
  814.         }
  815.  
  816.     q = i/r;
  817.  
  818.     if (q != 0)
  819.         mlputi(q, r);
  820.  
  821.     (*term.t_putchar)(hexdigits[i%r]);
  822.     ++ttcol;
  823.     }
  824.  
  825. /*
  826.  * do the same except as a long integer.
  827.  */
  828. mlputli(l, r)
  829.     long l;
  830.     {
  831.     register long q;
  832.  
  833.     if (l < 0)
  834.         {
  835.         l = -l;
  836.         (*term.t_putchar)('-');
  837.         }
  838.  
  839.     q = l/r;
  840.  
  841.     if (q != 0)
  842.         mlputli(q, r);
  843.  
  844.     (*term.t_putchar)((int)(l%r)+'0');
  845.     ++ttcol;
  846.     }
  847.  
  848. #if RAINBOW
  849.  
  850. putline(row, col, buf)
  851.     int row, col;
  852.     char buf[];
  853.     {
  854.     int n;
  855.  
  856.     n = strlen(buf);
  857.     if (col + n - 1 > term.t_ncol)
  858.         n = term.t_ncol - col + 1;
  859.     Put_Data(row, col, n, buf);
  860.     }
  861. #endif
  862.  
  863.